// Copyright (C) Kumo inc. and its affiliates.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//

#include <cantor/type/registry.h>
#include <turbo/strings/str_join.h>
#include <turbo/strings/str_split.h>
#include <turbo/strings/ascii.h>

namespace cantor {

    turbo::Status TypeRegistry::register_type(std::string_view uri, const TypePtr &type) {
        if (uri.empty()) {
            return turbo::invalid_argument_error("uri is empty");
        }
        auto lower = turbo::str_to_lower(uri);
        auto it = _type_map.find(lower);
        if (it != _type_map.end()) {
            return turbo::already_exists_error(lower, " is already exists");
        }

        _type_map[lower] = type;
        return turbo::OkStatus();
    }

    turbo::Status TypeRegistry::remove_type(std::string_view uri, bool not_found_as_ok) {
        if (uri.empty()) {
            return turbo::invalid_argument_error("uri is empty");
        }
        auto lower = turbo::str_to_lower(uri);
        auto it = _type_map.find(lower);
        if (it == _type_map.end()) {
            if (not_found_as_ok) {
                return turbo::OkStatus();
            }
            return turbo::not_found_error(lower);
        }
        _type_map.erase(lower);
        return turbo::OkStatus();
    }

    turbo::Result<TypePtr> TypeRegistry::resolve_type(std::string_view uri, bool root_first) const {
        if (uri.empty()) {
            return turbo::invalid_argument_error("uri is empty");
        }
        auto lower = turbo::str_to_lower(uri);
        auto it = _type_map.find(lower);
        if (it == _type_map.end()) {
            std::vector<std::string_view> uris = turbo::str_split(uri, ".");
            if (uris.size() <= 1) {
                return turbo::not_found_error(lower, " type not found");
            }
            /// uris.size <= 4
            if (uris.size() >= 4) {
                return turbo::invalid_argument_error(lower, " too many layer of type uri");
            }
            for (auto sit : uris) {
                if (sit.empty()) {
                    return turbo::invalid_argument_error(lower, " have empty scope name");
                }
            }
            auto type_str = uris.back();
            uris.pop_back();
            uris.pop_back();

            if (root_first) {
                return resolve_fragment_root_first(uris, type_str, lower);
            } else {
                return resolve_fragment_root_last(uris, type_str, lower);
            }
        }
        return it->second;
    }

    /// uris.size <= 4
    turbo::Result<TypePtr> TypeRegistry::resolve_fragment_root_first(const std::vector<std::string_view> &uris, std::string_view type_str, std::string_view full_query) const {
        /// global scope
        auto it = _type_map.find(type_str);
        if (it != _type_map.end()) {
            return it->second;
        }
        /// namespace scope we have already searched
        if (uris.empty()) {
            return turbo::not_found_error(full_query, " type not found");
        }
        /// namespace scope
        auto ns_scope_type_str = turbo::str_cat(uris.front(), ".", type_str);
        it = _type_map.find(ns_scope_type_str);
        if (it != _type_map.end()) {
            return it->second;
        }
        /// database scope we have already searched
        if (uris.size() <2) {
            return turbo::invalid_argument_error(full_query, " type not found");
        }
        auto db_type_str = turbo::str_cat(uris[0], ".", uris[1], ".", type_str);
        it = _type_map.find(db_type_str);
        if (it != _type_map.end()) {
            return it->second;
        }
        return turbo::not_found_error(db_type_str, " type not found");
    }

    turbo::Result<TypePtr> TypeRegistry::resolve_fragment_root_last(const std::vector<std::string_view> &uris, std::string_view type_str, std::string_view full_query) const {
        /// may be db scope
        auto scope_str =turbo::str_join(uris, ".");
        scope_str = turbo::str_cat(scope_str, ".", type_str);
        auto it = _type_map.find(scope_str);
        if (it != _type_map.end()) {
            return it->second;
        }

        if (uris.size() == 1) {
            it = _type_map.find(type_str);
            if (it != _type_map.end()) {
                return it->second;
            }
            return turbo::invalid_argument_error(full_query, " type not found");
        }
        /// maybe ns scope
        auto ns_type_str = turbo::str_cat(uris.front(), ".", type_str);
        it = _type_map.find(ns_type_str);
        if (it != _type_map.end()) {
            return it->second;
        }
        it = _type_map.find(type_str);
        if (it != _type_map.end()) {
            return it->second;
        }
        return turbo::not_found_error(full_query, " type not found");
    }

}  // namespace cantor
